
function resolve(value){
    if (this.status === 'pending') {
        this.value = value;
        this.status = 'fulfilled';
        this.onResolvedCallbacks.forEach(fn=>{
            fn();
        })
    }
}
function reject(reason){
    if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
        this.onRejectedCallbacks.forEach(fn=>{
            fn();
        })
    }
}

function resolvePromise(promise2,x,resolve,reject){
    if (promise2 === x) {
        throw new TypeError('Chain cycle detected for promise #<Promise>');
    }
    let called;
    if (x != null && typeof x === 'object' || typeof x === 'function') {
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x,(y)=>{
                    if (!called) {
                        called = true;
                    }else{
                        return;
                    }
                    resolvePromise(promise2,y,resolve,reject);
                },(r)=>{
                    if (!called) {
                        called = true;
                    }else{
                        return;
                    }
                    reject(r);
                })
            }else{
                resolve(x);
            }
        } catch (e) {
            if (!called) {
                called = true;
            }else{
                return;
            }
            reject(e);
        }
    }else{
        resolve(x);
    }
}

class Promise{
    constructor(executor){
        let self = this;
        self.status = 'pending'
        self.value = undefined;
        self.reason = undefined;
        self.onResolvedCallbacks = [];
        self.onRejectedCallbacks = [];
        try {
            executor(resolve.bind(self),reject.bind(self));
        } catch (e) {
            reject(e);
        }
    }


    then(onFulfilled,onRejected){
        let self = this;
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=>value;
        onRejected = typeof onRejected === 'function' ? onFulfilled : reason=>{
            throw reason;
        };
        let promise2;
        promise2 = new Promise((resolve,reject)=>{
            if (self.status === 'fulfilled') {
                setTimeout(() => {
                    try {
                        let x =onFulfilled(self.value);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }else if (self.status === 'rejected') {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }else if (self.status === 'pending') {
                self.onResolvedCallbacks.push(()=>{
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(self.value);
                            resolvePromise(promise2,x,resolve,reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
                self.onRejectedCallbacks.push(()=>{
                    setTimeout(() => {
                        try {
                            let x = onRejected(self.reason);
                            resolvePromise(promise2,x,resolve,reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
            }
        })
        return promise2;    
    }
    
    static resolve(value){
        return new Promise((resolve,reject)=>{
            resolve(value);
        })
    }
    static reject(value){
        return new Promise((resolve,reject)=>{
            reject(value);
        })
    }
    static all(promises){
        return new Promise((resolve,reject)=>{
            let arr = [];
            let j = 0;
            let processData = (index,data)=>{
                arr[index] = data;
                if (++j === promises.length) {
                    resolve(arr);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                let p = promises[i];                
                if (typeof p.then === 'function') {
                    p.then((value)=>{
                        processData(i,value);
                    },reject);
                }else{
                    processData(i,value);
                }
            }
        })
    }
    static race(promises){
        return new Promise((resolve,rejecy)=>{
            for (let i = 0; i < promises.length; i++) {
                let p = promises[i];
                if (typeof p.then === 'function') {
                    p.then(value=>{
                        resolve(value);           
                    },reject);
                }else{
                    resolve(x);
                }
            }
        })
    }
    catch(onRejected){
        return this.then(null,onRejected);
    }
    finally(onFinally){
        return this.then(value=>{
            onFinally();
            return value;
        },reason=>{
            onFinally();
            return reason;
        })
    }
}

module.exports = Promise;